# coding=utf-8

import json

from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet
from social.utils.hashid import decode_model_ids
from rest_framework.generics import get_object_or_404
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, DestroyModelMixin, CreateModelMixin, \
    UpdateModelMixin as OriginUpdateModelMixin
from . import logger
from django.http import Http404


class BaseAPIView(APIView):
    """
    返回json统一封装处理, 方便客户端解析
    """

    def finalize_response(self, request, response, *args, **kwargs):
        response = super(BaseAPIView, self).finalize_response(request, response, *args, **kwargs)
        if isinstance(response, Response):
            data = response.data
            if isinstance(data, list):
                data = {
                    'list': data
                }
            if response.status_code < 300:
                response.data = {
                    'code': 0,
                    'message': 'success',
                    'data': data
                }
        return response


class UpdateModelMixin(OriginUpdateModelMixin):
    def update(self, request, *args, **kwargs):
        kwargs['partial'] = kwargs.pop('partial', True)
        if self.can_update_fields or self.ban_update_fields:
            request._full_data = self.get_update_data(request)
        return super(UpdateModelMixin, self).update(request, *args, **kwargs)


class BaseGenericViewSet(GenericViewSet):
    lookup_url_kwarg = "hid"
    lookup_field = "pk"
    model_label = ""
    already_decode_hid = False
    allows_uid_lookup = False
    can_update_fields = ()
    ban_update_fields = ()

    def get_update_data(self, request) -> dict:
        """
        过滤字段 实现可更新字段功能
        :param request:
        :return:
        """
        if self.ban_update_fields:
            return {k: v for k, v in request.data.items() if k not in self.ban_update_fields}
        elif self.can_update_fields:
            return {k: v for k, v in request.data.items() if k in self.can_update_fields}
        else:
            return request.data

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        serializer_class = getattr(self, "serializer_{action}_class".format(action=self.action),
                                   self.serializer_class)
        assert serializer_class is not None, (
                "'%s' should either include a `serializer_class` attribute, "
                "or override the `get_serializer_class()` method."
                % self.__class__.__name__
        )
        return serializer_class

    def initialize_request(self, request, *args, **kwargs):
        if self.already_decode_hid:
            return super(BaseGenericViewSet, self).initialize_request(request, *args, **kwargs)
        hid: str = self.kwargs.get(self.lookup_url_kwarg, None)
        pk = kwargs.get("pk", None)
        if hid and hid.isdigit() and self.allows_uid_lookup:
            self.kwargs['uid'] = int(hid)
        elif hid and pk is None:
            pk, label = decode_model_ids(hid,
                                         self.queryset.model._meta.label if self.model_label == "" else self.model_label,
                                         with_label=True)
            self.kwargs['model_label'] = kwargs['model_label'] = label
            self.kwargs[self.lookup_field] = kwargs[self.lookup_field] = pk

        return super(BaseGenericViewSet, self).initialize_request(request, *args, **kwargs)

    def get_object(self):

        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert 'pk' in self.kwargs or 'hid' in self.kwargs or 'uid' in self.kwargs, (
                'Expected view %s to be called with a URL keyword argument '
                'named "%s". Fix your URL conf, or set the `.lookup_field` '
                'attribute on the view correctly.' %
                (self.__class__.__name__, lookup_url_kwarg)
        )
        if self.kwargs.get('uid'):
            filter_kwargs = {'uid': self.kwargs['uid']}
        else:
            filter_kwargs = {self.lookup_field: self.kwargs[self.lookup_field]}

        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj
